/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxDiscreteSurface.h>

Sx::Graphics::Objects::DiscreteSurface::DiscreteSurface() {
	this->faces = Util::IndexedFaceArray(0);
	this->vertexNormals = Util::VectorArray(0);
	this->faceNormals = Util::VectorArray(0);
}

Sx::Graphics::Objects::DiscreteSurface::DiscreteSurface(const DiscreteSurface& surface) : Surface(surface) {
	this->faces = Util::IndexedFaceArray(surface.faces);
	this->vertexNormals = Util::VectorArray(surface.vertexNormals);
	this->faceNormals = Util::VectorArray(surface.faceNormals);
}

Sx::Graphics::Objects::DiscreteSurface::~DiscreteSurface() {}

void Sx::Graphics::Objects::DiscreteSurface::clear() {
	this->faces.clear();
	this->faceNormals.clear();
	this->vertexNormals.clear();
}

void Sx::Graphics::Objects::DiscreteSurface::addFace(const Primitives::IndiceFace& face) {
	this->faces.add(face);
}

bool Sx::Graphics::Objects::DiscreteSurface::setFace(const Primitives::IndiceFace& face, unsigned int index) {
	if ( this->faces.size() == 0 ) {
		std::cerr << "[DiscreteSurface:setFace] Error: This surface contains no faces." << std::endl;
		std::cerr << "  Surface has not been modified." << std::endl;
		return false;
	}

	if ( index >= this->faces.size() ) {
		std::cerr << "[DiscreteSurface:setFace] Error: Face index out of bounds: " << index << " (last valid index: " << this->faces.size() - 1 << std::endl;
		std::cerr << "  Surface has not been modified." << std::endl;
		return false;
	}

	this->faces[index] = face;
	return true;
}

bool Sx::Graphics::Objects::DiscreteSurface::remove(const Primitives::IndiceFace& face) {
	return this->faces.remove(face);
}

bool Sx::Graphics::Objects::DiscreteSurface::removeFace(const Primitives::IndiceFace& face) {
	return this->faces.remove(face);
}

bool Sx::Graphics::Objects::DiscreteSurface::removeFace(unsigned int index) {
	return this->faces.removeAt(index);
}

bool Sx::Graphics::Objects::DiscreteSurface::triangulateFaces() {
	if ( this->faces.size() == 0 ) {
		std::cerr << "[DiscreteSurface:triangulateFaces] Error: Cannot triangulate surface with no faces." << std::endl;
		std::cerr << "  Discrete surface has not been modified." << std::endl;
		return false;
	}
	
	//--------------------------------------------------------------------------
	// Calculate the number of new triangle faces (this is because it is not
	// assumed that all faces have the same degree).
	//--------------------------------------------------------------------------
	unsigned int triangleFaceCount = 0;
	for ( unsigned int i = 0; i < this->faces.size(); i++ )
		triangleFaceCount += this->faces[i].indices.size() - 2;

	Util::ArrayList<Primitives::IndiceFace> triangleFaces;

	//--------------------------------------------------------------------------
	// Triangulate All Faces and store them in a new triangulated face array.
	//--------------------------------------------------------------------------
	Primitives::IndiceFace triangle;
	for ( unsigned int i = 0; i < this->faces.size(); i++ ) {
		Primitives::IndiceFace& curFace = this->faces[i];

		unsigned int index = 0;
		triangle.indices = Util::ArrayList<unsigned int>(3);
		for ( unsigned int k = 0; k < curFace.indices.size() - 2; k++ ) {
			triangle.indices[2] = curFace.indices[0];
			triangle.indices[1] = curFace.indices[index + 2];
			triangle.indices[0] = curFace.indices[index + 1];
			triangleFaces.add(triangle);
			index++;
		}
	}

	//--------------------------------------------------------------------------
	// Copy new triangle faces to mesh faces array.
	//--------------------------------------------------------------------------
	this->faces = triangleFaces;
	return true;
}

Sx::Graphics::Util::IndexedFaceArray& Sx::Graphics::Objects::DiscreteSurface::getFaces() {
	return this->faces;
}

const Sx::Graphics::Util::IndexedFaceArray& Sx::Graphics::Objects::DiscreteSurface::getFaces() const {
	return this->faces;
}

Sx::Graphics::Util::VectorArray& Sx::Graphics::Objects::DiscreteSurface::getNormals(Math::PrimitiveType type) {
	if ( type == Math::FACE ) return this->faceNormals;
	if ( type == Math::VERTEX ) return this->vertexNormals;
	
	std::cerr << "[DiscreteSurface:getNormals] Error: Cannot retrieve primitive type specified." << std::endl;
	std::cerr << "  Returning vertex normals array." << std::endl;
	return this->vertexNormals;
}

Sx::Graphics::Util::VectorArray& Sx::Graphics::Objects::DiscreteSurface::getVertexNormals() {
	return this->vertexNormals;
}

const Sx::Graphics::Util::VectorArray& Sx::Graphics::Objects::DiscreteSurface::getVertexNormals() const {
	return this->vertexNormals;
}

Sx::Graphics::Util::VectorArray& Sx::Graphics::Objects::DiscreteSurface::getFaceNormals() {
	return this->faceNormals;
}

const Sx::Graphics::Util::VectorArray& Sx::Graphics::Objects::DiscreteSurface::getFaceNormals() const {
	return this->faceNormals;
}

unsigned int Sx::Graphics::Objects::DiscreteSurface::getFaceCount() const {
	return this->faces.size();
}

unsigned int Sx::Graphics::Objects::DiscreteSurface::getNormalCount(Math::PrimitiveType type) const {
	if ( type == Math::FACE ) return this->faceNormals.size();
	if ( type == Math::VERTEX ) return this->vertexNormals.size();
	return 0;
}

unsigned int Sx::Graphics::Objects::DiscreteSurface::getVertexNormalCount() const {
	return this->vertexNormals.size();
}

unsigned int Sx::Graphics::Objects::DiscreteSurface::getFaceNormalCount() const {
	return this->faceNormals.size();
}

Sx::Graphics::Primitives::IndiceFace Sx::Graphics::Objects::DiscreteSurface::getFace(unsigned int index) {
	if ( index >= this->faces.size() ) {
		std::cerr << "[DiscreteSurface:getFace] Error: Face index out of bounds: " << index << std::endl;
		return Primitives::IndiceFace();
	}
	
	return this->faces[index];
}

Eigen::Vector3f Sx::Graphics::Objects::DiscreteSurface::getNormal(Math::PrimitiveType type, unsigned int index) const {
	if ( type == Math::FACE ) {
		if ( index >= this->faceNormals.size() ) {
			std::cerr << "[DiscreteSurface:getNormal] Error: Normal index out of bounds: " << index << std::endl;
			return Eigen::Vector3f::Zero();
		}
		return this->faceNormals[index];
	}

	if ( type == Math::VERTEX ) {
		if ( index >= this->vertexNormals.size() ) {
			std::cerr << "[DiscreteSurface:getNormal] Error: Normal index out of bounds: " << index << std::endl;
			return Eigen::Vector3f::Zero();
		}
		return this->vertexNormals[index];
	}

	std::cerr << "[DiscreteSurface:getNormal] Error: Unsupported normal type." << std::endl;
	return Eigen::Vector3f::Zero();
}

Eigen::Vector3f Sx::Graphics::Objects::DiscreteSurface::getFaceNormal(unsigned int index) const {
	if ( index >= this->faceNormals.size() ) {
		std::cerr << "[DiscreteSurface:getFaceNormal] Error: Face index out of bounds: " << index << std::endl;
		return Eigen::Vector3f();
	}

	return this->faceNormals[index];
}

Eigen::Vector3f Sx::Graphics::Objects::DiscreteSurface::getVertexNormal(unsigned int index) const {
	if ( index >= this->vertexNormals.size() ) {
		std::cerr << "[DiscreteSurface:getFaceNormal] Error: Vertex index out of bounds: " << index << std::endl;
		return Eigen::Vector3f();
	}

	return this->vertexNormals[index];
}

Sx::Graphics::Objects::DiscreteSurface& Sx::Graphics::Objects::DiscreteSurface::operator = (const DiscreteSurface& surface) {
	if ( this == &surface ) return *this;
	this->faces = surface.faces;
	this->faceNormals = surface.faceNormals;
	this->vertexNormals = surface.vertexNormals;
	return *this;
}
